package com.acxy.ants.core;

import com.acxy.ants.common.enums.DataSourceType;
import com.acxy.ants.common.kit.PropKit;
import com.acxy.ants.common.kit.StrKit;
import com.acxy.ants.core.annotation.Autowired;
import com.acxy.ants.core.annotation.Source;
import com.acxy.ants.core.annotation.Value;
import com.acxy.ants.core.config.Services;
import com.acxy.ants.core.kit.ParamTypeKit;
import com.acxy.ants.core.plugin.db.Db;
import com.acxy.ants.core.proxy.CGLibProxy;
import com.acxy.ants.resful.spring.util.ReflectionUtils;

import javax.sql.DataSource;
import java.lang.reflect.Field;

/**
 * 类里面的属性字段绑定
 * @author MrShun
 * @version 1.0
 * @Date 2017-05-15
 */
public class AnnotationBinding {

    public static void initFiledValues(Object object) {
        Class cls = object.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                ReflectionUtils.makeAccessible(field);
                String key = "";
                Object service = null;
                try {
                    Class targetClass = field.getAnnotation(Autowired.class).value();
                    Class typeClass= field.getType();
                    //判断是接口还是实现类
                    if (typeClass.isInterface() && targetClass == Autowired.class) {
                        //默认直接实例化第一个接口
                        service = Services.getService(typeClass);
                        if(service == null) throw new RuntimeException(cls + " 中含有@Service注解类中, 不能注入接口!");
                    } else {
                        String cName = targetClass == Autowired.class?typeClass.getName():targetClass.getName();
                        key = StrKit.lastSubstring(cName, '.', 2).replace(".", "_");
                        service = Services.getService(key);
                        if (service == null) {
                            service = Class.forName(cName).newInstance();
                            AnnotationBinding.initFiledValues(service);
                            //采用CGLIB代理
                            Object proxy = CGLibProxy.createProxy(service);
                            //初始化完成后调用init()实例化
                            if (proxy instanceof InitializingBean) {
                                InitializingBean ib = (InitializingBean) service;
                                ib.init();
                            }
                            Services.setService(key, proxy);
                        }
                    }
                    field.set(object, service);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }catch (InstantiationException e) {
                    e.printStackTrace();
                    throw new RuntimeException(cls +" " + key + " 实例化失败, 请认真检查!");
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            } else if (field.isAnnotationPresent(Value.class)) {
                try {
                    Value v = field.getAnnotation(Value.class);
                    String[] values = v.value().split("!");
                    Object paramValue;
                    try {
                        paramValue = PropKit.get(values[0]);
                    } catch (Exception e) {
                        throw new RuntimeException(cls + " 没有加载任何配置文件, 却注入了属性!");
                    }
                    if (paramValue == null && values.length > 1)
                        paramValue = StrKit.join(values, "!", 1);
                    Object objectValue = ParamTypeKit.parse(String.valueOf(paramValue), field.getType());
                    ReflectionUtils.makeAccessible(field);
                    field.set(object, objectValue);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } else if (field.isAnnotationPresent(Source.class)) {
                try {
                    ReflectionUtils.makeAccessible(field);
                    Source source = field.getAnnotation(Source.class);
                    DataSource dataSource = DataSourceType.getDataSource(source);
                    if (field.getType() == Db.class)
                        field.set(object, new Db(dataSource));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
